#!/usr/bin/env python3
"""
分析Java插件与Go服务的通信协议
"""

import subprocess
import os
import zipfile
import re
from pathlib import Path

def extract_jar_safely():
    """安全地提取JAR文件"""
    jar_file = "lingma-jetbrains/lib/cosy-intellij-2.5.16.jar"
    extract_dir = "extracted_jar"
    
    if os.path.exists(extract_dir):
        import shutil
        shutil.rmtree(extract_dir)
    
    os.makedirs(extract_dir)
    
    print(f"提取 {jar_file} 到 {extract_dir}")
    
    with zipfile.ZipFile(jar_file, 'r') as zip_ref:
        zip_ref.extractall(extract_dir)
    
    return extract_dir

def find_communication_classes(extract_dir):
    """查找通信相关的类"""
    print("\n🔍 查找通信相关的类文件...")
    
    communication_keywords = [
        "client", "server", "request", "response", "socket", "websocket",
        "http", "api", "connect", "service", "proxy", "agent", "lsp"
    ]
    
    class_files = []
    for root, dirs, files in os.walk(extract_dir):
        for file in files:
            if file.endswith('.class'):
                filepath = os.path.join(root, file)
                filename_lower = file.lower()
                
                # 检查是否包含通信关键词
                for keyword in communication_keywords:
                    if keyword in filename_lower:
                        class_files.append(filepath)
                        break
    
    print(f"找到 {len(class_files)} 个相关类文件:")
    for cf in class_files[:20]:
        print(f"  {cf}")
    
    return class_files

def decompile_class(class_file):
    """使用CFR反编译class文件"""
    print(f"\n反编译: {class_file}")
    
    # 检查是否有cfr.jar
    cfr_path = "cfr.jar"
    if not os.path.exists(cfr_path):
        print("下载CFR反编译器...")
        try:
            import urllib.request
            urllib.request.urlretrieve(
                "https://github.com/leibnitz27/cfr/releases/latest/download/cfr-0.152.jar",
                cfr_path
            )
        except:
            print("CFR下载失败，尝试使用javap...")
            return decompile_with_javap(class_file)
    
    try:
        result = subprocess.run([
            'java', '-jar', cfr_path, class_file
        ], capture_output=True, text=True, timeout=30)
        
        if result.returncode == 0:
            return result.stdout
        else:
            print(f"CFR反编译失败: {result.stderr}")
            return decompile_with_javap(class_file)
            
    except Exception as e:
        print(f"CFR运行失败: {e}")
        return decompile_with_javap(class_file)

def decompile_with_javap(class_file):
    """使用javap反编译"""
    try:
        # 获取类名
        class_name = class_file.replace('extracted_jar/', '').replace('.class', '').replace('/', '.')
        
        result = subprocess.run([
            'javap', '-cp', 'extracted_jar', '-c', '-p', class_name
        ], capture_output=True, text=True, timeout=30)
        
        if result.returncode == 0:
            return result.stdout
        else:
            print(f"javap失败: {result.stderr}")
            return None
            
    except Exception as e:
        print(f"javap运行失败: {e}")
        return None

def analyze_communication_patterns(source_code):
    """分析通信模式"""
    if not source_code:
        return []
    
    patterns = []
    
    # 查找端口信息
    port_pattern = r'(\d{4,5})'
    ports = re.findall(port_pattern, source_code)
    if ports:
        patterns.append(f"可能的端口: {set(ports)}")
    
    # 查找URL模式
    url_pattern = r'(https?://[^\s"\']+|localhost[^\s"\']*|127\.0\.0\.1[^\s"\']*)'
    urls = re.findall(url_pattern, source_code)
    if urls:
        patterns.append(f"URL: {set(urls)}")
    
    # 查找WebSocket相关
    if 'websocket' in source_code.lower() or 'ws://' in source_code.lower():
        patterns.append("使用WebSocket通信")
    
    # 查找LSP相关
    if 'lsp' in source_code.lower() or 'language server' in source_code.lower():
        patterns.append("使用LSP协议")
    
    # 查找JSON-RPC
    if 'jsonrpc' in source_code.lower() or 'json-rpc' in source_code.lower():
        patterns.append("使用JSON-RPC")
    
    # 查找方法调用
    method_pattern = r'(\w+)\s*\([^)]*\)\s*\{'
    methods = re.findall(method_pattern, source_code)
    important_methods = [m for m in methods if any(k in m.lower() for k in ['send', 'request', 'call', 'invoke', 'connect'])]
    if important_methods:
        patterns.append(f"重要方法: {set(important_methods)}")
    
    return patterns

def search_config_files(extract_dir):
    """搜索配置文件"""
    print("\n📄 搜索配置文件...")
    
    config_files = []
    for root, dirs, files in os.walk(extract_dir):
        for file in files:
            if any(file.endswith(ext) for ext in ['.properties', '.yml', '.yaml', '.json', '.xml']):
                config_files.append(os.path.join(root, file))
    
    print(f"找到 {len(config_files)} 个配置文件:")
    
    for config_file in config_files:
        print(f"\n--- {config_file} ---")
        try:
            with open(config_file, 'r', encoding='utf-8') as f:
                content = f.read()
                
                # 查找端口、URL等信息
                if any(keyword in content.lower() for keyword in ['port', 'url', 'host', 'endpoint', 'socket']):
                    print(content[:500] + ("..." if len(content) > 500 else ""))
        except:
            pass

def analyze_startup_classes():
    """分析启动相关类"""
    print("\n🚀 分析启动流程...")
    
    startup_classes = [
        "extracted_jar/com/alibabacloud/intellij/cosy/LingmaStartupActivity.class",
        "extracted_jar/com/alibabacloud/intellij/cosy/LingmaApplication.class"
    ]
    
    for class_file in startup_classes:
        if os.path.exists(class_file):
            print(f"\n=== {class_file} ===")
            source = decompile_class(class_file)
            if source:
                # 查找关键信息
                lines = source.split('\n')
                for i, line in enumerate(lines):
                    if any(keyword in line.lower() for keyword in ['port', 'localhost', '127.0.0.1', 'socket', 'connect']):
                        # 打印前后几行上下文
                        start = max(0, i-2)
                        end = min(len(lines), i+3)
                        print(f"第{i+1}行附近:")
                        for j in range(start, end):
                            marker = ">>> " if j == i else "    "
                            print(f"{marker}{lines[j]}")
                        print()

def find_go_interaction():
    """查找与Go程序交互的代码"""
    print("\n🔗 查找Go程序交互代码...")
    
    # 搜索可能的进程启动代码
    extract_dir = "extracted_jar"
    
    # 查找包含"Lingma"、"process"、"exec"等关键词的类
    for root, dirs, files in os.walk(extract_dir):
        for file in files:
            if file.endswith('.class'):
                filepath = os.path.join(root, file)
                
                # 反编译并搜索
                source = decompile_class(filepath)
                if source and any(keyword in source for keyword in ['ProcessBuilder', 'Runtime.exec', 'Lingma', 'socket', 'localhost']):
                    print(f"\n=== {filepath} ===")
                    
                    # 提取相关行
                    lines = source.split('\n')
                    for i, line in enumerate(lines):
                        if any(keyword in line for keyword in ['ProcessBuilder', 'Runtime.exec', 'new Socket', 'localhost', 'port']):
                            print(f"第{i+1}行: {line.strip()}")

def main():
    """主函数"""
    print("=" * 60)
    print("Java插件通信协议分析")
    print("=" * 60)
    
    # 1. 提取JAR文件
    extract_dir = extract_jar_safely()
    
    # 2. 查找通信相关类
    comm_classes = find_communication_classes(extract_dir)
    
    # 3. 分析关键类
    key_classes = comm_classes[:5]  # 分析前5个最相关的类
    
    communication_info = {}
    
    for class_file in key_classes:
        print(f"\n{'='*50}")
        print(f"分析: {class_file}")
        print('='*50)
        
        source = decompile_class(class_file)
        if source:
            patterns = analyze_communication_patterns(source)
            communication_info[class_file] = patterns
            
            print("发现的通信模式:")
            for pattern in patterns:
                print(f"  • {pattern}")
            
            # 保存反编译结果
            filename = class_file.replace('/', '_').replace('.class', '.java')
            with open(f'decompiled_{filename}', 'w') as f:
                f.write(source)
    
    # 4. 搜索配置文件
    search_config_files(extract_dir)
    
    # 5. 分析启动类
    analyze_startup_classes()
    
    # 6. 查找Go交互
    find_go_interaction()
    
    # 7. 总结
    print("\n" + "="*60)
    print("分析总结")
    print("="*60)
    
    print("\n发现的通信信息:")
    for class_file, patterns in communication_info.items():
        if patterns:
            print(f"\n{class_file}:")
            for pattern in patterns:
                print(f"  • {pattern}")
    
    print(f"\n反编译的源码文件:")
    for f in os.listdir('.'):
        if f.startswith('decompiled_') and f.endswith('.java'):
            print(f"  • {f}")

if __name__ == "__main__":
    main()